import { defineStore } from 'pinia';
import { computed, reactive, ref } from 'vue';
import type { PriceComponent } from '@/types';
import { uniqueId, cloneDeep, clone } from 'lodash';
import {
  deletePriceComponents,
  getPriceComponents,
  postPriceComponents,
  putPriceComponents,
} from '@/api';
import {
  mapComponentsForBackend,
  mapComponentsForFrontend,
} from '@/stores/priceComponentStore/utils';

export const usePriceComponentStore = defineStore('priceComponent', () => {
  const components = reactive<Record<number, PriceComponent>>({});
  const isLoading = ref(false);
  const shouldApiFail = ref(false);

  const nextOrder = computed(
    () =>
      Object.values(components).reduce<number>((order, current) => {
        if (current.order > order) {
          return current.order;
        }

        return order;
      }, 0) + 1
  );

  const addComponent = async (
    component: Omit<PriceComponent, 'id' | 'order'>
  ) => {
    const id = Number(uniqueId());
    components[id] = { ...component, id, order: nextOrder.value };
  };

  const removeComponent = async (id: number) => {
    const componentToDelete = clone(components[id]);

    delete components[id];

    try {
      await deletePriceComponents(
        mapComponentsForBackend([componentToDelete]),
        shouldApiFail.value
      );
    } catch (e) {
      console.log(e);
      components[id] = componentToDelete;
    }
  };

  const updateComponent = async (
    id: number,
    component: Partial<Omit<PriceComponent, 'id'>>
  ) => {
    const componentToUpdate = clone(components[id]);

    components[id] = { ...components[id], ...component };

    if (component.isNew) {
      return;
    }

    try {
      if (typeof component.isNew === 'boolean') {
        await putPriceComponents(
          mapComponentsForBackend([components[id]])[0],
          shouldApiFail.value
        );
      } else {
        await postPriceComponents(
          mapComponentsForBackend([components[id]]),
          shouldApiFail.value
        );
      }
    } catch (e) {
      console.log(e);
      components[id] = componentToUpdate;
    }
  };

  const bulkUpdate = async (
    componentsToUpdate: (Pick<PriceComponent, 'id'> & Partial<PriceComponent>)[]
  ) => {
    const backup = cloneDeep(components);

    componentsToUpdate.forEach(
      (component) =>
        (components[component.id] = {
          ...components[component.id],
          ...component,
        })
    );

    try {
      await postPriceComponents(
        mapComponentsForBackend(Object.values(components)),
        shouldApiFail.value
      );
    } catch (e) {
      console.log(e);
      Object.values(backup).forEach(
        (component) => (components[component.id] = component)
      );
    }
  };

  const getComponents = async () => {
    isLoading.value = true;
    try {
      const currentComponents = await getPriceComponents(shouldApiFail.value);
      Object.keys(components).forEach(
        (key: string) => delete components[Number(key)]
      );
      mapComponentsForFrontend(currentComponents).forEach(
        (component) => (components[component.id] = component)
      );
    } catch (e) {
      console.log(e);
    } finally {
      isLoading.value = false;
    }
  };

  const toggleShouldApiFail = () => {
    shouldApiFail.value = !shouldApiFail.value;
  };

  return {
    components,
    isLoading,
    shouldApiFail,
    getComponents,
    addComponent,
    removeComponent,
    updateComponent,
    bulkUpdate,
    toggleShouldApiFail,
  };
});
